package se.sll.itintegration.catalogue.gui.camel.cdi;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.endsWith;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import javax.ws.rs.core.SecurityContext;

import org.apache.camel.Endpoint;
import org.apache.camel.Expression;
import org.apache.camel.builder.ExpressionBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.apache.commons.io.IOUtils;
import org.junit.Before;

import se.sll.itintegration.catalogue.gui.entity.CareUnitEntity;
import se.sll.itintegration.catalogue.gui.entity.UserEntity;
import se.sll.itintegration.catalogue.gui.service.HsaCache;
import se.sll.service.catalogue.common.rest.response.HsaData;

/**
 * Base class for CamelRoute test cases.
 * 
 * @author Christer Palm, Nogui
 */
public abstract class CamelRouteTestBase extends CamelTestSupport {
    public static class MockInitialContextFactory implements InitialContextFactory {
        private HsaCache hsaCache;

        public MockInitialContextFactory() {
            HsaData hsaData1 = new HsaData();
            hsaData1.setRelativeDistinguishedName("unitId1Name");

            HsaData hsaData2 = new HsaData();
            hsaData2.setRelativeDistinguishedName("unitId2Name");

            hsaCache = mock(HsaCache.class);
            when(hsaCache.getHsa("hsaID1")).thenReturn(hsaData1);
            when(hsaCache.getHsa("hsaID2")).thenReturn(hsaData2);
        }

        @Override
        public Context getInitialContext(Hashtable<?, ?> arg) throws NamingException {
            Context context = mock(Context.class);
            when(context.lookup(endsWith("/GUI-HSACache"))).thenReturn(hsaCache);
            return context;
        }
    }

    private static final Expression TESTDATA_PRODUCT_PRODUCTS = readTestData("productProducts.json");
    private static final Expression TESTDATA_OFFERS_UNITS = readTestData("offersUnits.json");
    private static final Expression TESTDATA_OFFERS_UNITS_BYUNITIDS = readTestData("offersUnitsByunitids.json");
    private static final Expression TESTDATA_CAPACITY_CAPACITY = readTestData("capacityCapacity.json");
    private static final Expression TESTDATA_CAPACITY_CAPACITY_BYUNITIDS = readTestData("capacityCapacityByunitids.json");

    protected Principal principal;
    protected SecurityContext securityContext;
    protected MockEndpoint productProductsEndpoint;
    protected MockEndpoint offersUnitsEndpoint;
    protected MockEndpoint offersUnitsByunitidsEndpoint;
    protected MockEndpoint capacityCapacityEndpoint;
    protected MockEndpoint capacityCapacityByunitidsEndpoint;
    protected CareUnitEntity userEjbFindByUsernameCareUnit1;
    protected Set<CareUnitEntity> userEjbFindByUsernameCareUnits;
    protected UserEntity userEjbFindByUsernameResult1;
    protected List<UserEntity> userEjbFindByUsernameResult;
    protected MockEndpoint userEjbFindByUsernameEndpoint;

    public CamelRouteTestBase() {
        System.setProperty("java.naming.factory.initial", MockInitialContextFactory.class.getName());
    }

    @Override
    public String isMockEndpointsAndSkip() {
        return "^http4:.*|^ejb:.*";
    };

    @Before
    public void init() throws Exception {
        principal = mock(Principal.class);
        when(principal.getName()).thenReturn("username");

        securityContext = mock(SecurityContext.class);
        when(securityContext.getUserPrincipal()).thenReturn(principal);
        when(securityContext.isUserInRole("SUPERADMIN")).thenReturn(true);

        printAvailableMockEndpoints();

        productProductsEndpoint = getMockEndpoint("mock://http4:product/products", false);
        productProductsEndpoint.returnReplyBody(TESTDATA_PRODUCT_PRODUCTS);

        offersUnitsEndpoint = getMockEndpoint("mock://http4:offers/units", false);
        offersUnitsEndpoint.returnReplyBody(TESTDATA_OFFERS_UNITS);

        offersUnitsByunitidsEndpoint = getMockEndpoint("mock://http4:offers/units/byunitids", false);
        offersUnitsByunitidsEndpoint.returnReplyBody(TESTDATA_OFFERS_UNITS_BYUNITIDS);

        capacityCapacityEndpoint = getMockEndpoint("mock://http4:capacity/capacity", false);
        capacityCapacityEndpoint.returnReplyBody(TESTDATA_CAPACITY_CAPACITY);

        capacityCapacityByunitidsEndpoint = getMockEndpoint("mock://http4:capacity/capacity/byunitids", false);
        capacityCapacityByunitidsEndpoint.returnReplyBody(TESTDATA_CAPACITY_CAPACITY_BYUNITIDS);

        userEjbFindByUsernameCareUnit1 = mock(CareUnitEntity.class);
        when(userEjbFindByUsernameCareUnit1.getHsaID()).thenReturn("hsaID1");

        userEjbFindByUsernameCareUnits = new HashSet<>();
        userEjbFindByUsernameCareUnits.add(userEjbFindByUsernameCareUnit1);

        userEjbFindByUsernameResult1 = mock(UserEntity.class);
        when(userEjbFindByUsernameResult1.getCareUnits()).thenReturn(userEjbFindByUsernameCareUnits);
        
        userEjbFindByUsernameResult = new ArrayList<>();
        userEjbFindByUsernameResult.add(userEjbFindByUsernameResult1);

        if (context.hasEndpoint("mock://ejb:java:module/UserEjb") != null) {
            userEjbFindByUsernameEndpoint = getMockEndpoint("mock://ejb:java:module/UserEjb", false);
        } else {
            userEjbFindByUsernameEndpoint = getMockEndpoint("mock://ejb:java:global/gui/UserEjb", false);
        }
        userEjbFindByUsernameEndpoint.returnReplyBody(ExpressionBuilder.constantExpression(userEjbFindByUsernameResult));
    }
    
    @Override
    protected RouteBuilder createRouteBuilder() throws Exception {
        return new CamelRoute();
    }

    private void printAvailableMockEndpoints() {
        System.out.println("Available mock endpoints:");
        for (Endpoint endpoint : context.getEndpoints()) {
            if (endpoint instanceof MockEndpoint) {
                System.out.println("    " + endpoint);
            }
        }
    }

    private static Expression readTestData(String filename) {
        try {
            String resource = "se/sll/itintegration/catalogue/gui/camel/cdi/" + filename;
            InputStream in = CamelRouteTestBase.class.getClassLoader().getResourceAsStream(resource);
            if (in == null) {
                throw new RuntimeException("Missing resource: " + resource);
            }
            byte[] buf = IOUtils.toByteArray(in);
            Expression expr = ExpressionBuilder.constantExpression(new String(buf, StandardCharsets.UTF_8));
            System.out.println(expr);
            return expr;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
